﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace cz.zcu.PreSti
{
     public class Presentation : IPresentation
     {
         private Dictionary<string, ISymbols> symbols;
         public SortedSet<RunnableComponent> Layers = new SortedSet<RunnableComponent>();
         public List<RunnableComponent> EntryPoints = new List<RunnableComponent>();
         private DateTime startTime;
         private bool isLoading = true;
         private bool isTerminating = false;
         private int loadedSymbols = 0;
         private string lastError = null;

         public List<RunnableComponent> entryPoints
         {
             get
             {
                 return EntryPoints;
             }
             set
             {
                 EntryPoints = value;
             }
         }

         public SortedSet<RunnableComponent> layers
         {
             get
             {
                 return Layers;
             }
         }

         public DateTime StartTime
         {
             get
             {
                 return startTime;
             }
         }


         public bool IsLoading
         {
             get
             {
                 return isLoading;
             }
         }

         public bool IsTerminating
         {
             get
             {
                 return isTerminating;
             }
         }

         public int LoadedSymbols
         {
             get
             {
                 return loadedSymbols;
             }
         }

         public int SymbolsCount
         {
             get
             {
                 int count = 0;
                 foreach (ISymbols ss in symbols.Values)
                     count += ss.GetSymbolsCount();
                 return count;
             }
         }

         public string LastError
         {
             get
             {
                 string error = lastError;
                 lastError = null;
                 return error;
             }
             set
             {
                 lastError = value;
             }
         }

         public bool HasError
         {
             get
             {
                 return lastError != null;
             }
         }

         public Presentation(Dictionary<string, ISymbols> symbols)
         {
             this.symbols = symbols;
         }

         public void run()
         {
             startTime = DateTime.Now;
             foreach (RunnableComponent entryPoint in entryPoints)
                 entryPoint.trigIn(0);
         }

         public void initComponents()
         {
             isLoading = true;
             Dictionary<ISymbol, BaseComponent> dict = new Dictionary<ISymbol,BaseComponent>();
             foreach (ISymbol s in symbols["1"].GetEntryPoints()) 
             {
                 loadedSymbols++;
                 entryPoints.Add((RunnableComponent)(s.initializeComponent(this)));
                 dict.Add(s, entryPoints.Last());
                 observeChildsR(dict, s, "1");
             }
             isLoading = false;
         }

         public void initSubdiagramComponents(
             string diagramname,
             ref List<RunnableComponent> entryPoints, ref List<RunnableComponent> exitPoints,
             ref List<RunnableComponent> setters, ref List<RunnableComponent> getters) 
         {
             if (diagramname == "")
             {
                 Console.Out.WriteLine("Wrong diagram name!");
             }
             else
             {
                 isLoading = true;
                 Dictionary<ISymbol, BaseComponent> dict = new Dictionary<ISymbol, BaseComponent>();
                 /*
                  * Firstly, initializes all getters, setters, entry and exit points
                  * to build it's lists and provide it out of method (by "ref" keyword).
                  */
                 foreach (ISymbol s in symbols[diagramname].GetSetters())
                 {
                     loadedSymbols++;
                     setters.Add((RunnableComponent)(s.initializeComponent(this)));
                     dict.Add(s, setters.Last());
                 }
                 foreach (ISymbol s in symbols[diagramname].GetGetters())
                 {
                     loadedSymbols++;
                     getters.Add((RunnableComponent)(s.initializeComponent(this)));
                     dict.Add(s, getters.Last());
                 }
                 foreach (ISymbol s in symbols[diagramname].GetEntryPoints())
                 {
                     loadedSymbols++;
                     entryPoints.Add((RunnableComponent)(s.initializeComponent(this)));
                     dict.Add(s, entryPoints.Last());
                 }
                 foreach (ISymbol s in symbols[diagramname].GetExitPoints())
                 {
                     loadedSymbols++;
                     exitPoints.Add((RunnableComponent)(s.initializeComponent(this)));
                     dict.Add(s, exitPoints.Last());
                 }
                 /*
                  * Secondly, observes all entry points' childs.
                  */
                 foreach (ISymbol s in symbols[diagramname].GetEntryPoints())
                     observeChildsR(dict, s, diagramname);
                 /*
                  * Thirtly, observes all getters' childs, because it's parts of subdiagram
                  * don't need to be connected to the rest necessarily, but have to be
                  * processed, because it's output values could be required out of subdiagram.
                  */
                 foreach (ISymbol s in symbols[diagramname].GetGetters())
                     observeChildsR(dict, s, diagramname);
                 isLoading = false;
             }
         }

         private void observeChildsR(Dictionary<ISymbol, BaseComponent> dict, ISymbol s, string diagramname)
         {
             BaseComponent curComp = dict[s];
             List<ITrigger> triggers = symbols[diagramname].GetTriggers(s);
             foreach (ITrigger t in triggers)
             {
                 loadedSymbols++;
                 if (t.IsDescendantConnected())
                 {
                     ISymbol otherS = t.GetDescendant();
                     RunnableComponent otherComp;
                     if (dict.ContainsKey(otherS))
                     {
                         otherComp = (RunnableComponent)dict[otherS];
                     }
                     else
                     {
                         otherComp = (RunnableComponent)(otherS.initializeComponent(this));
                         otherComp.Redraw += new EventHandler(onRedraw);
                         loadedSymbols++;
                     }
                     otherComp.update();
                     ((RunnableComponent)curComp).setTrigOut(t.GetEventN(), otherComp, t.GetConnectedShapeActionN(), t.GetDelay());
                     if (!dict.ContainsKey(otherS))
                     {
                         dict.Add(otherS, otherComp);
                         observeChildsR(dict, otherS, diagramname);
                     }
                 }
             }
             List<IConnection> connections = symbols[diagramname].GetSetterConnections(s);
             foreach (IConnection c in connections)
             {
                 loadedSymbols++;
                 if (c.IsFullyConnected() && curComp is RunnableComponent) //Odstranit druhou část podmínky
                 {
                     ISymbol otherS = c.GetConnectedShape();
                     BaseComponent otherComp;
                     if (dict.ContainsKey(otherS))
                     {
                         otherComp = dict[otherS];
                     }
                     else
                     {
                         otherComp = (BaseComponent)(otherS.initializeComponent(this));
                         loadedSymbols++;
                     }
                     curComp.setupSetter(c.GetSetterN(), otherComp, c.GetConnectedShapeGetterN());
                     if (!dict.ContainsKey(otherS))
                     {
                         dict.Add(otherS, otherComp);
                         observeChildsR(dict, otherS, diagramname);
                     }
                 }
             }
         }

         public void terminate()
         {
             foreach (RunnableComponent c in entryPoints)
             {
                 terminate(c);
             }
             if (Terminated != null) Terminated(this, EventArgs.Empty);
         }

         private void terminate(RunnableComponent c)
         {
             foreach (RunnableComponent.Trigger t in c.TrigOuts)
             {
                 t.terminate();
                 t.Component.terminate();
                 terminate(t.Component);
             }
         }

         public event EventHandler Terminated;
         public event EventHandler Redraw;

         private void onRedraw(object sender, EventArgs e)
         {
             if (Redraw != null) Redraw(sender, e);
         }

        public virtual bool supportsGraphics()
        {
            return true;
        }

        public virtual bool supportsAudio()
        {
            return false;
        }

        public virtual void initSound()
        {
            // not supported
        }

        public virtual void playSound(String Ps_soundName)
        {
            // not supported
        }
    }
}
